package com.sxj.springsourcecode.springframework.factory.support;

import com.sxj.springsourcecode.springframework.exception.BeanException;
import com.sxj.springsourcecode.springframework.factory.PropertyValue;
import com.sxj.springsourcecode.springframework.factory.PropertyValues;
import com.sxj.springsourcecode.springframework.factory.config.*;
import com.sxj.springsourcecode.springframework.factory.support.strategy.CglibInstantiationStrategy;
import com.sxj.springsourcecode.springframework.factory.support.strategy.InstantiationStrategy;
import com.sxj.springsourcecode.springframework.factory.support.strategy.JdkInstantiationStrategy;
import com.sxj.springsourcecode.springframework.utils.ClassUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: spring-source-code
 * @Package: com.sxj.springsourcecode.springframework.factory.support
 * @ClassName: AbstractAutowireBeanFactory
 * @Author: sxj
 * @Description:
 * @Date: 2024/2/21 11:37
 * @Version: 1.0.0
 */
@Slf4j
public abstract class AbstractAutowireBeanFactory extends AbstractBeanFactory{
    private InstantiationStrategy strategy = new CglibInstantiationStrategy();

    @Override
    public Object createBean(String beanName, BeanDefinition beanDefinition,Object[] args) {
        log.info("创建bean:{}",beanName);
        //1 初始化对象
        Object bean = null;
        try {
            bean = createBeanInstance(beanDefinition.getClz(),args);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BeanException("实例化对象出错！");
        }

        //2 填充属性
        applyPropertyValues(bean,beanDefinition);

        //3 初始化对象
        initialzeBean(beanName,beanDefinition,bean);

        //4 注册 销毁方法
        registerDisposableBeanIfNecesscery(beanName,bean,beanDefinition);

        //2 保存到单例容器
        addSingleton(beanName,bean);
        return bean;
    }

    public Object createBeanInstance(Class clz,Object[] args) throws Exception{
        Constructor ctrToUse = null;
        //1 先根据参数，找到指定的构造器
        Constructor[] constructors = clz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            if (args != null && constructor.getParameterCount() == args.length){
                ctrToUse = constructor;
                break;
            }
        }

        return getStrategy().instantBean(clz,ctrToUse,args);
    }

    public InstantiationStrategy getStrategy() {
        return strategy;
    }

    /**
     * 设置字段属性
     * @param bean
     * @param beanDefinition
     */
    private void applyPropertyValues(Object bean,BeanDefinition beanDefinition){
        PropertyValues pvs = beanDefinition.getPvs();
        if (pvs != null && !pvs.isEmpty()){
            for (PropertyValue pv : pvs.getPropertyValues()) {
                String name = pv.getName();
                Object value = pv.getValue();
                if (value instanceof BeanRefrence){
                    BeanRefrence ref = (BeanRefrence) value;
                    value = getBean(ref.getRefName());
                }
                ClassUtil.setFeildValue(beanDefinition.getClz(),bean,name,value);
            }
        }
    }

    public abstract void addBeanPostProcessor(BeanPostProcessor v);

    /**
     * TODO 
     * @param type
     */
    public abstract <T> Map<String, T> getBeansOfType(Class<T> type);

    public abstract List<BeanPostProcessor> getBeanPostProcessors();

    /**
     * 初始化bean对象
     * @param beanName
     * @param beanDefinition
     * @param bean
     */
    private Object initialzeBean(String beanName,BeanDefinition beanDefinition,Object bean){
        Object wrapperbean = applyBeanPostProcessBeforeInitialization(bean,beanName);

        //执行初始化
        invokeInitMethods(wrapperbean,beanDefinition);

        wrapperbean = applyBeanPostProcessAftereInitialization(bean,beanName);
        return wrapperbean;

    }

    /**
     * 应用后置处理方法
     * @param existingBean
     * @param beanName
     */
    private Object applyBeanPostProcessBeforeInitialization(Object existingBean,String beanName){
        Object result = existingBean;
        List<BeanPostProcessor> processors = getBeanPostProcessors();
        for (BeanPostProcessor processor : processors) {
            Object current = processor.postProcessBeforeInitialization(result, beanName);
            if (current == null){
                break;
            }else {
                result = current;
            }
        }
        return result;
    }

    /**
     * 应用后置方法
     * @param existingBean
     * @param beanName
     */
    private Object applyBeanPostProcessAftereInitialization(Object existingBean,String beanName){
        Object result = existingBean;
        List<BeanPostProcessor> processors = getBeanPostProcessors();
        for (BeanPostProcessor processor : processors) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            result = current;
            if (result == null) {
                break;
            }
        }
        return result;
    }

    /**
     * 调用初始化方法
     * @param existingBean
     * @param beanDefinition
     */
    private void invokeInitMethods(Object existingBean,BeanDefinition beanDefinition){
        //1 实现接口 InitializationBean
        if (existingBean instanceof InitializationBean){
            InitializationBean bean = (InitializationBean) existingBean;
            bean.afterPropertiesSet();
        }

        //2 定义了初始化方法
        String initMethodName = beanDefinition.getInitMethodName();
        if (StringUtils.hasText(initMethodName)){
            ClassUtil.invokeMethod(beanDefinition.getClz(),existingBean,initMethodName);
        }
    }


    /**
     * TODO 
     *
     */
    public void registerDisposableBeanIfNecesscery(String beanName,Object bean,BeanDefinition beanDefinition){
        if (bean instanceof DisposableBean || StringUtils.hasText(beanDefinition.getDestroyMethodName())){
            registerDispoableBean(beanName,bean,beanDefinition);
        }
    }

    public abstract void registerDispoableBean(String beanName,Object bean,BeanDefinition beanDefinition);
}
